/*
 * Copyright (c) 2024 endless-sky
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "aac_pcm_decoder.h"

#include "utils_log.h"

#include <cstdio>
#include <cstring>
#include <mutex>

namespace El {
namespace Muxer {

namespace {

int GetBytesPerSample(AacPcmDecoder::PcmFormat format)
{
    switch (format) {
        case AacPcmDecoder::PcmFormat::S16LE:
            return 2;
        case AacPcmDecoder::PcmFormat::S32LE:
        case AacPcmDecoder::PcmFormat::FLT:
            return 4;
        default:
            return 2;
    }
}

std::once_flag g_gst_once_flag;

void InitGStreamer()
{
    EL_INFO("Initializing GStreamer for AAC decoder...");
    gst_init(nullptr, nullptr);
    EL_INFO("GStreamer initialized successfully");
}

bool EnsureGStreamerInitialized()
{
    try {
        std::call_once(g_gst_once_flag, InitGStreamer);
    } catch (...) {
        EL_ERROR("Failed to initialize GStreamer runtime");
        return false;
    }
    return true;
}

} // namespace

AacPcmDecoder::AacPcmDecoder() = default;

AacPcmDecoder::~AacPcmDecoder()
{
    if (pipeline_) {
        gst_element_set_state(pipeline_, GST_STATE_NULL);
        gst_object_unref(pipeline_);
        pipeline_ = nullptr;
    }
    appsrc_ = nullptr;
    appsink_ = nullptr;
}

const char* AacPcmDecoder::GetGstSampleFormat(PcmFormat format) const
{
    switch (format) {
        case PcmFormat::S16LE:
            return "S16LE";
        case PcmFormat::S32LE:
            return "S32LE";
        case PcmFormat::FLT:
            return "F32LE";
        default:
            return nullptr;
    }
}

bool AacPcmDecoder::BuildPipeline(int sample_rate, int channels, PcmFormat format)
{
    const char* fmt = GetGstSampleFormat(format);
    if (!fmt) {
        EL_ERROR("Unsupported output PCM format");
        return false;
    }

    // 构建 GStreamer 管道: AAC (ADTS) -> PCM
    // aacparse: 解析AAC ADTS格式
    // avdec_aac: FFmpeg的AAC解码器
    // audioconvert + audioresample: 格式和采样率转换
    char pipeline_desc[1024];
    snprintf(pipeline_desc, sizeof(pipeline_desc),
             "appsrc name=aac_source format=time is-live=false "
             "! aacparse "
             "! avdec_aac "
             "! audioconvert "
             "! audioresample "
             "! audio/x-raw,format=%s,rate=%d,channels=%d,layout=interleaved "
             "! appsink name=pcm_sink emit-signals=false sync=false drop=false max-buffers=32",
             fmt, sample_rate, channels);

    EL_INFO("Creating AAC decoder pipeline: {}", pipeline_desc);

    GError* error = nullptr;
    pipeline_ = gst_parse_launch(pipeline_desc, &error);
    if (!pipeline_ || error) {
        EL_ERROR("Failed to create GStreamer decoder pipeline: {}", error ? error->message : "unknown error");
        if (error) {
            g_error_free(error);
        }
        return false;
    }

    appsrc_ = gst_bin_get_by_name(GST_BIN(pipeline_), "aac_source");
    appsink_ = gst_bin_get_by_name(GST_BIN(pipeline_), "pcm_sink");

    if (!appsrc_ || !appsink_) {
        EL_ERROR("Failed to get appsrc or appsink from decoder pipeline");
        return false;
    }

    // 设置 appsrc 的流类型
    gst_app_src_set_stream_type(GST_APP_SRC(appsrc_), GST_APP_STREAM_TYPE_STREAM);

    // 设置为 PAUSED 状态
    GstStateChangeReturn state_ret = gst_element_set_state(pipeline_, GST_STATE_PAUSED);
    if (state_ret == GST_STATE_CHANGE_FAILURE) {
        EL_ERROR("Failed to set decoder pipeline to PAUSED state");
        return false;
    }

    // 等待到达 PAUSED 状态
    GstState state = GST_STATE_VOID_PENDING;
    GstState pending = GST_STATE_VOID_PENDING;
    state_ret = gst_element_get_state(pipeline_, &state, &pending, 5 * GST_SECOND);

    EL_INFO("Decoder pipeline state after PAUSED request: result={}, current={}, pending={}",
            static_cast<int>(state_ret), static_cast<int>(state), static_cast<int>(pending));

    if (state_ret == GST_STATE_CHANGE_FAILURE) {
        EL_ERROR("Decoder pipeline state change to PAUSED failed");

        // 获取错误信息
        GstBus* bus = gst_element_get_bus(pipeline_);
        if (bus) {
            GstMessage* msg;
            while ((msg = gst_bus_pop(bus)) != nullptr) {
                GError* err = nullptr;
                gchar* debug_info = nullptr;

                switch (GST_MESSAGE_TYPE(msg)) {
                    case GST_MESSAGE_ERROR:
                        gst_message_parse_error(msg, &err, &debug_info);
                        EL_ERROR("GStreamer decoder error from {}: {} (debug: {})",
                                GST_OBJECT_NAME(msg->src),
                                err->message,
                                debug_info ? debug_info : "none");
                        g_error_free(err);
                        g_free(debug_info);
                        break;
                    case GST_MESSAGE_WARNING:
                        gst_message_parse_warning(msg, &err, &debug_info);
                        EL_WARN("GStreamer decoder warning from {}: {} (debug: {})",
                               GST_OBJECT_NAME(msg->src),
                               err->message,
                               debug_info ? debug_info : "none");
                        g_error_free(err);
                        g_free(debug_info);
                        break;
                    default:
                        break;
                }
                gst_message_unref(msg);
            }
            gst_object_unref(bus);
        }

        return false;
    }

    // 设置为 PLAYING 状态
    state_ret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);
    if (state_ret == GST_STATE_CHANGE_FAILURE) {
        EL_ERROR("Failed to set decoder pipeline to PLAYING state");
        return false;
    }

    if (state_ret != GST_STATE_CHANGE_FAILURE) {
        EL_INFO("Decoder pipeline set to PLAYING state (return: {})", static_cast<int>(state_ret));
    }

    return true;
}

bool AacPcmDecoder::Initialize(int output_sample_rate, int output_channels, PcmFormat pcm_format)
{
    if (initialized_) {
        EL_WARN("AacPcmDecoder already initialized");
        return true;
    }

    if (!EnsureGStreamerInitialized()) {
        return false;
    }

    pcm_format_ = pcm_format;
    output_sample_rate_ = output_sample_rate;
    output_channels_ = output_channels;
    output_bytes_per_sample_ = GetBytesPerSample(pcm_format_);
    bytes_pushed_ = 0;
    eos_pushed_ = false;
    decoder_flushed_ = false;

    if (pipeline_) {
        gst_element_set_state(pipeline_, GST_STATE_NULL);
        gst_object_unref(pipeline_);
        pipeline_ = nullptr;
        appsrc_ = nullptr;
        appsink_ = nullptr;
    }

    if (!BuildPipeline(output_sample_rate_, output_channels_, pcm_format_)) {
        if (pipeline_) {
            gst_element_set_state(pipeline_, GST_STATE_NULL);
            gst_object_unref(pipeline_);
        }
        pipeline_ = nullptr;
        appsrc_ = nullptr;
        appsink_ = nullptr;
        return false;
    }

    initialized_ = true;
    EL_INFO("AacPcmDecoder initialized using GStreamer (AAC -> PCM)");
    return true;
}

bool AacPcmDecoder::PushBuffer(const uint8_t* data, size_t size)
{
    if (!appsrc_ || !data || size == 0) {
        return true;
    }

    if (eos_pushed_) {
        EL_WARN("PushBuffer called after EOS was sent on decoder");
        return false;
    }

    GstBuffer* buffer = gst_buffer_new_allocate(nullptr, size, nullptr);
    if (!buffer) {
        EL_ERROR("Failed to allocate GstBuffer for AAC data");
        return false;
    }

    GstMapInfo map;
    if (!gst_buffer_map(buffer, &map, GST_MAP_WRITE)) {
        EL_ERROR("Failed to map GstBuffer for writing AAC data");
        gst_buffer_unref(buffer);
        return false;
    }

    std::memcpy(map.data, data, size);
    gst_buffer_unmap(buffer, &map);

    // AAC数据不需要PTS计算(由解码器自动处理)
    bytes_pushed_ += size;

    GstFlowReturn flow = gst_app_src_push_buffer(GST_APP_SRC(appsrc_), buffer);
    if (flow != GST_FLOW_OK) {
        EL_ERROR("Failed to push AAC buffer into appsrc (flow={})", static_cast<int>(flow));
        return false;
    }

    return true;
}

bool AacPcmDecoder::PullSamples(std::vector<uint8_t>& out, bool drain)
{
    if (!appsink_) {
        return false;
    }

    const GstClockTime timeout = drain ? GST_CLOCK_TIME_NONE : 0;

    while (true) {
        GstSample* sample = gst_app_sink_try_pull_sample(GST_APP_SINK(appsink_), timeout);
        if (!sample) {
            break;
        }

        GstBuffer* buffer = gst_sample_get_buffer(sample);
        if (buffer) {
            GstMapInfo map;
            if (gst_buffer_map(buffer, &map, GST_MAP_READ)) {
                out.insert(out.end(), map.data, map.data + map.size);
                gst_buffer_unmap(buffer, &map);
            } else {
                EL_ERROR("Failed to map decoded GstBuffer for reading");
                gst_sample_unref(sample);
                return false;
            }
        }
        gst_sample_unref(sample);
    }

    if (drain && gst_app_sink_is_eos(GST_APP_SINK(appsink_))) {
        decoder_flushed_ = true;
    }

    return true;
}

bool AacPcmDecoder::ProcessInput(const uint8_t* data, size_t size)
{
    if (!initialized_) {
        EL_ERROR("AacPcmDecoder not initialized");
        return false;
    }

    if (decoder_flushed_) {
        EL_WARN("ProcessInput called after decoder was flushed");
        return false;
    }

    if (!PushBuffer(data, size)) {
        return false;
    }

    return true;
}

bool AacPcmDecoder::GetData(std::vector<uint8_t>& buffer, bool flush)
{
    buffer.clear();

    if (!initialized_) {
        EL_ERROR("AacPcmDecoder not initialized");
        return false;
    }

    if (flush && !decoder_flushed_ && !eos_pushed_) {
        GstFlowReturn flow = gst_app_src_end_of_stream(GST_APP_SRC(appsrc_));
        if (flow != GST_FLOW_OK) {
            EL_ERROR("Failed to send EOS to decoder appsrc (flow={})", static_cast<int>(flow));
            return false;
        }
        eos_pushed_ = true;
    }

    if (!PullSamples(buffer, flush)) {
        return false;
    }

    if (flush && gst_app_sink_is_eos(GST_APP_SINK(appsink_))) {
        decoder_flushed_ = true;
    }

    return true;
}

} // namespace Muxer
} // namespace El
